package whut.pso;

import org.apache.commons.math3.util.MathArrays;
import whut.commons.IProblemSet;
import whut.commons.Location;

import java.util.Set;
import java.util.TreeSet;

/**
 * Created by Justin on 2018-04-03
 */
public class PSOProcessM extends PSOProcess{
    private Set<Location> history;
    private double LocRangeThresh;

    public PSOProcessM(IProblemSet problem, INormalization normalize, IWeightHandler weightHandler,
                       IUpdateSwarm updateSwarm) {
        super(problem, normalize, weightHandler, updateSwarm);
        history = new TreeSet<>();
    }

    private void initpBest(){
        for (int i = 0; i < SWARM_SIZE; i++) {
            pBest[i] = fitnessValueList[i];
            pBestLocation.add(swarm.get(i).getLocation());
        }
    }

    public Set<Location> execute(int count, double thresh) {
        clear();

        initializeSwarm(null, 0);
        updateFitnessList();
        initpBest();
        gBestLocation = null;

        int n = 0;
        Location candidate = null;
        if (thresh <= 0) {
            LocRangeThresh = 0.1;
        } else {
            LocRangeThresh = thresh;     //default is 0.1;
        }

        while (history.size() < count && n < 100 * count){
            mLog.info("\n---> {}/{} :",history.size(), n++);
            Location bestLoc = searching();

            if (bestLoc != null){
//                //固定的范围
//                if (!history.contains(bestLoc)){
//                    history.add(bestLoc);
//                }
                // 自适应的范围，可以设定一个比较大的排除范围。
                if (candidate == null && !history.contains(bestLoc)){
                    candidate = bestLoc;

                    clear();
                    initializeSwarm(candidate.getLoc(), LocRangeThresh);
                    updateFitnessList();
                    initpBest();
                    continue;
                }else if (candidate != null && !history.contains(bestLoc)){
                    double dist = MathArrays.distance(candidate.getLoc(), bestLoc.getLoc());
                    if (dist < 1e-3){
                        history.add(bestLoc);
                        candidate = null;

                        clear();
                        initializeSwarm(null, 0);
                        updateFitnessList();
                        initpBest();
                        continue;
                    }else{
                        LocRangeThresh = 0.8 * LocRangeThresh;
//                        System.out.println("locRange = " + LocRangeThresh);
                        mLog.warn("locRange = {}", LocRangeThresh);

                        clear();
                        initializeSwarm(candidate.getLoc(), LocRangeThresh);
                        updateFitnessList();
                        initpBest();
                        continue;
                    }
                }
            }
            clear();
            initializeSwarm(null, 0);
            updateFitnessList();
            initpBest();
        }

        return history;
    }
    
    private double[] variation(double[] value){
        int PROBLEM_DIMENSION = problem.getPROBLEM_DIMENSION();

        for (Location item : history){
            double dist = MathArrays.distance(item.getLoc(), value);//dm.compute();
            if (dist < LocRangeThresh){
                double[] loc = new double[PROBLEM_DIMENSION];
                for (int j = 0; j < PROBLEM_DIMENSION; j++) {
                    loc[j] = problem.getLOC_LOW() + generator.nextDouble() * (problem.getLOC_HIGH() - problem.getLOC_LOW());
                }
                double[] normLoc = normalize.normalizeLocationOfRange(loc,null,0);
                return normLoc;
            }
        }
        return value;
    }

    @Override
    public double[] afterUpdateLocation(Particle p, double[] v) {
        return  variation(v);
    }
}
